/**
 * GenJ - GenealogyJ
 *
 * Copyright (C) 1997 - 2002 Nils Meier <nils@meiers.net>
 *
 * This piece of code is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or (at your option) any later version.
 *
 * This code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package genj.option;

import genj.util.Registry;
import genj.util.Resources;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * An option based on a simple accessible value
 */
public abstract class PropertyOption extends Option {

    /** property */
    protected String property;

    /** option is for instance */
    protected Object instance;

    /**
     * Get options for given instance
     */
    public static List<PropertyOption> introspect(Object instance) {
	return introspect(instance, false);
    }

    public static List<PropertyOption> introspect(Object instance, boolean recursively) {
	return introspect(instance, recursively ? new ArrayList<Object>() : null);
    }

    private static List<PropertyOption> introspect(Object instance,
	    List<Object> trackingRecursivelyVisited) {

	// tracking visited?
	if (trackingRecursivelyVisited != null)
	    trackingRecursivelyVisited.add(instance);

	// prepare result
	List<PropertyOption> result = new ArrayList<PropertyOption>();
	Set<String> beanattrs = new HashSet<String>();

	// loop over bean properties of instance
	try {
	    BeanInfo info = Introspector.getBeanInfo(instance.getClass());
	    PropertyDescriptor[] properties = info.getPropertyDescriptors();
	    for (int p = 0; p < properties.length; p++) {

		PropertyDescriptor property = properties[p];

		try {
		    // has to have getter & setter
		    if (property.getReadMethod() == null || property.getWriteMethod() == null)
			continue;

		    // // int, boolean, String?
		    // if (!Impl.isSupportedArgument(property.getPropertyType()))
		    // continue;

		    // try a read
		    property.getReadMethod().invoke(instance, (Object[]) null);

		    // create
		    // result.add(BeanPropertyImpl.create(instance, property));

		    // remember name
		    beanattrs.add(property.getName());
		} catch (Throwable t) {
		}
	    }
	} catch (IntrospectionException e) {
	}

	// loop over fields of instance
	Field[] fields = instance.getClass().getFields();
	for (int f = 0; f < fields.length; f++) {

	    Field field = fields[f];
	    Class<?> type = field.getType();

	    // won't address name of property again
	    if (beanattrs.contains(field.getName()))
		continue;

	    // has to be public, non-static, non-final
	    int mod = field.getModifiers();
	    if (Modifier.isFinal(mod) || Modifier.isStatic(mod))
		continue;
	    try {
		field.get(instance);
	    } catch (Throwable t) {
		continue;
	    }

	    // // int, boolean, String?
	    // if (Impl.isSupportedArgument(type)) {
	    // result.add(FieldImpl.create(instance, field));
	    // } else
	    {
		// could still be recursive
		if (trackingRecursivelyVisited != null)
		    try {
			for (PropertyOption recursiveOption : introspect(field.get(instance),
				trackingRecursivelyVisited)) {
			    String cat = recursiveOption.getCategory();
			    recursiveOption.setCategory(field.getName());
			    result.add(recursiveOption);
			}
		    } catch (Throwable t) {
			// ignore it
		    }
	    }

	    // next
	}

	// done
	return result;
    }

    /**
     * Constructor
     */
    protected PropertyOption(Object instance, String property) {
	this.instance = instance;
	this.property = property;
    }

    /**
     * Persist
     */
    public abstract void persist(Registry registry);

    /**
     * Persist
     */
    public abstract void restore(Registry registry);

    /**
     * Accessor - option value
     */
    public abstract Object getValue();

    /**
     * Accessor - option value
     */
    public abstract void setValue(Object set);

    /**
     * Setter - name
     */
    public abstract void setName(String set);

    /**
     * Setter - tool tip
     */
    public abstract void setToolTip(String set);

    /**
     * Accessor - a unique key for this option
     */
    public String getProperty() {
	return property;
    }

    /**
     * Accessor - category of this option
     */
    @Override
    public String getCategory() {
	String result = super.getCategory();
	if (result == null) {
	    // try to localize?
	    Resources resources = Resources.get(instance);
	    result = resources.getString("options", false);
	    if (result != null)
		super.setCategory(result);
	}
	return result;
    }

} // ValueOption
